package com.atguigu.tingshu.album.service.impl;

import brave.propagation.ThreadLocalCurrentTraceContext;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.common.config.redisson.RedissonConfig;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.protobuf.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.reactive.config.EnableWebFlux;

import java.io.DataInput;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;

    @Autowired
    private AlbumStatMapper albumStatMapper;

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private RabbitService rabbitService;


    /**
     * 提供内容创作者/运营人员保存专辑
     * 保存专辑的方法
     * 1.将提交专辑 VO 转化为 PO 对象，新增专辑
     * 2.将提交专辑标签封装为专辑标签关系集合对象，进行批量保存
     * 3.为新增专辑标签，批量保存专辑统计信息
     *
     * @param albumInfoVo
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)// 默认事务注解修饰方法捕获运行时异常或者 Error
    @Override
    public void saveAlbumInfo(Long userId, AlbumInfoVo albumInfoVo) {
        // 1.保存专辑
        // 1.1 将提交专辑 VO 拷贝到专辑 PO 对象中，仅限同名同类型属性才会被拷贝
        AlbumInfo albumInfo = new AlbumInfo();

        BeanUtils.copyProperties(albumInfoVo, albumInfo);

        // 1.2 手动为用户 ID 、包含声音数量(0) 、是否完结、免费试听集数、审核状态(TODO:审核通过)
        albumInfo.setUserId(userId);
        albumInfo.setIncludeTrackCount(0);
        albumInfo.setIsFinished("0");
        albumInfo.setTracksForFree(1);
        albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);

        // 1.3 保存专辑，得到专辑id
        albumInfoMapper.insert(albumInfo);
        Long id = albumInfo.getId();

        // 2.保存专辑标签列表
        // 2.1 将提交专辑标签关系表 VO 集合 转为 PO 集合
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();

        if (CollectionUtils.isNotEmpty(albumAttributeValueVoList)) {
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
                BeanUtils.copyProperties(albumAttributeValueVo, albumAttributeValue);
                albumAttributeValue.setAlbumId(id);
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }

        // 3.保存专辑统计信息
        this.saveAlbumStat(id, SystemConstant.ALBUM_STAT_BUY, 0);
        this.saveAlbumStat(id, SystemConstant.ALBUM_STAT_PLAY, 0);
        this.saveAlbumStat(id, SystemConstant.ALBUM_STAT_SUBSCRIBE, 0);
        this.saveAlbumStat(id, SystemConstant.ALBUM_STAT_COMMENT, 0);

        // 4.TODO 调用内容审核接口

        // 5.审核通过后发送上架专辑消息到 RabbitMq
        if ("1".equals(albumInfo.getIsOpen())) {
            // 上架 发送的内容是由谁来决定
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());

        } else {
            // 下架
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumInfo.getId());
        }

    }


    /**
     * 保存专辑统计信息
     *
     * @param albumId  专辑ID
     * @param statType 统计类型
     * @param statNum  统计数量
     */

    @Override
    public void saveAlbumStat(Long albumId, String statType, int statNum) {
        AlbumStat albumStat = new AlbumStat();
        albumStat.setAlbumId(albumId);
        albumStat.setStatType(statType);
        albumStat.setStatNum(statNum);
        albumStatMapper.insert(albumStat);
    }



	/**
     * 查询当前用户专辑分页列表
     *
     * @param pageInfo       分页对象
     * @param albumInfoQuery 查询条件对象
     * @return
     */
    @Override
    public Page<AlbumListVo> getUserAlbumPage(Page<AlbumListVo> pageInfo, AlbumInfoQuery albumInfoQuery) {
        return albumInfoMapper.getUserAlbumPage(pageInfo, albumInfoQuery);
    }

    /**
     * 根据专辑ID删除专辑
     *
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeAlbumInfo(Long id) {

        Long count = trackInfoMapper.selectCount(new LambdaQueryWrapper<TrackInfo>().eq(TrackInfo::getAlbumId, id));

        if (count > 0) {
            throw new GuiguException(400, "专辑下存在音轨文件");
        }
        // 删除专辑
        albumInfoMapper.deleteById(id);

        // 删除标签
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>()
                .eq(AlbumAttributeValue::getAlbumId, id));

        // 删除统计数据
        albumStatMapper.delete(new LambdaQueryWrapper<AlbumStat>()
                .eq(AlbumStat::getAlbumId, id));

        // 下架
        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, id);

    }

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 获取专辑信息（SpringDataRedis实现分布式锁）避免缓存击穿
     *
     * @param id
     * @return
     */
    @Override
    public AlbumInfo getAlbumInfo(Long id) {



        // 构建业务数据key
        try {
            String dataKey = RedisConstant.ALBUM_INFO_PREFIX + id;

            AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(dataKey);

            if (null != albumInfo){
                log.info("命中缓存，直接返回，线程ID：{}，线程名称：{}",Thread.currentThread().getId(),Thread.currentThread().getName());
                return albumInfo;
            }

            // 尝试获取分布式锁，构建锁 Key
            String lockKey = RedisConstant.ALBUM_INFO_PREFIX + id + RedisConstant.CACHE_LOCK_SUFFIX;

            // 基于RedissonClient创建锁对象 入参成为Redis中Hash结构锁的key
            RLock lock = redissonClient.getLock(lockKey);
            lock.lock();

            try {
                // 获取锁成功执行业务，将查询业务数据放入缓存redis
                // 处于阻塞装填等待获取线程（终将获取锁成功）避免获取锁线程再次查库，这里再查一次缓存
                albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(dataKey);
                if (null != null){
                    log.info("当前线程{}，获取锁成功，且再次命中缓存成功",Thread.currentThread().getName());
                    return albumInfo;
                }
                // 查询数据库将查询结果放入 Redis 缓存中 返回查询结果
                albumInfo = this.getAlbumInfoFromDB(id);
                long ttl = albumInfo == null ? RedisConstant.ALBUM_TEMPORARY_TIMEOUT : RedisConstant.ALBUM_TIMEOUT;
                redisTemplate.opsForValue().set(dataKey,albumInfo,ttl,TimeUnit.SECONDS);
                return albumInfo;
            } finally {
                log.info("当前线程：{}，释放锁",Thread.currentThread().getName());
                lock.unlock();
            }


            // 获取线程标识
//            String lockVal = IdUtil.fastSimpleUUID();
//
//            Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal, RedisConstant.ALBUM_LOCK_EXPIRE_PX2, TimeUnit.SECONDS);
//
//            if (flag){
//                // 获取锁成功则执行查询业务
//                log.info("获取锁成功：{}，线程名称：{}",Thread.currentThread().getId(), Thread.currentThread().getName());
//
//                try {
//                    albumInfo = this.getAlbumInfoFromDB(id);
//
//                    Long ttl = albumInfo == null ? RedisConstant.ALBUM_TEMPORARY_TIMEOUT : RedisConstant.ALBUM_TIMEOUT;
//
//                    redisTemplate.opsForValue().set(dataKey,albumInfo,ttl,TimeUnit.SECONDS);
//
//                    return albumInfo;
//                } finally {
//                    String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
//                            "then\n" +
//                            "    return redis.call(\"del\",KEYS[1])\n" +
//                            "else\n" +
//                            "    return 0\n" +
//                            "end";
//                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
//                    redisScript.setScriptText(script);
//                    redisScript.setResultType(Long.class);
//                    redisTemplate.execute(redisScript, Arrays.asList(lockKey)/*分布式锁key列表*/,lockVal);
//                }
//            }else {
//
//                try {
//                    Thread.sleep(200);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//
//                log.error("获取锁失败，自旋：{}，线程名称：{}",Thread.currentThread().getId(),Thread.currentThread().getName());
//
//                return this.getAlbumInfo(id);
//            }
        } catch (Exception e) {
            log.info("[专辑服务]redis服务异常：{}",e);
            return this.getAlbumInfoFromDB(id);
        }

    }

    /**
     * 根据ID从数据库中获取专辑信息
     * @param id
     * @return
     */
    @Override
    public AlbumInfo getAlbumInfoFromDB(Long id){
        AlbumInfo albumInfo = albumInfoMapper.selectById(id);

        //2.根据专辑ID查询专辑标签列表
        if (null != albumInfo){
            List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueMapper.selectList(
                    new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId,id)
            );
            albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
        }

        return albumInfo;
    }




    /**
     * 修改专辑信息
     *
     * @param id          专辑ID
     * @param albumInfoVo 修改专辑信息VO对象
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo) {
        AlbumInfo albumInfo = new AlbumInfo();

        BeanUtils.copyProperties(albumInfoVo, albumInfo);

        albumInfo.setId(id);

        albumInfoMapper.updateById(albumInfo);

        //2.修改专辑标签 先删再新增
        //2.1 根据专辑ID将以前关联标签记录全部删除
        albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>()
                .eq(AlbumAttributeValue::getAlbumId, id));

        //2.2 在根据提交专辑标签做新增
        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        // 为什么要做判断？
        if (CollectionUtils.isNotEmpty(albumAttributeValueVoList)) {
            //2.2 关联专辑ID批量保存专辑标签关系
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
                BeanUtils.copyProperties(albumAttributeValueVo, albumAttributeValue);
                albumAttributeValue.setAlbumId(id);
                albumAttributeValueMapper.insert(albumAttributeValue);
            }
        }

        // TODO 如果是开放专辑自动将专辑同步到 ES索引库中
        if ("1".equals((albumInfo.getIsOpen()))) {
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());
        } else {
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumInfo.getId());
        }


    }

    /**
     * 查询当前登录用户专辑列表
     *
     * @return
     */
    @Override
    public List<AlbumInfo> getUserAllAlbumList(Long userId) {

        List<AlbumInfo> albumInfoList = albumInfoMapper.selectList(new LambdaQueryWrapper<AlbumInfo>()
                .eq(AlbumInfo::getUserId, userId).orderByDesc(AlbumInfo::getId).last("limit 20"));

        return albumInfoList;
    }

    /**
     * 根据专辑 ID 获取专辑统计信息
     * @param albumId
     * @return
     */
    @Override
    public AlbumStatVo getAlbumStatVo(Long albumId) {

        AlbumStatVo albumStatVo = albumInfoMapper.getAlbumStatVo(albumId);

        return albumStatVo;
    }


}
